;----------------------------------------
;05_interrupts_traps.asm
;该工程参照《自己动手写操作系统》pmtest9 和 本仓库的04_page_switch
;主题是初始化8259A片设置IDT中断
;----------------------------------------
%include "pm.inc"


;程序正式开始
org 0100h
jmp LABEL_BEGIN

;GDT表，此处为了将执行程序和修改程序同时进行，设置了两个权限互斥的4G的段
[SECTION .gdt]
LABEL_GDT Descriptor 0,0,0
LABEL_DESC_CODE32 Descriptor 0,SegCode32Len-1,DA_CR|DA_32
LABEL_DESC_STACK32 Descriptor 0,TopOfStack,DA_DRWA|DA_32
LABEL_DESC_CODE16 Descriptor 0,0ffffh,DA_C
LABEL_DESC_NORMAL Descriptor 0,0ffffh,DA_DRW
LABEL_DESC_DATA Descriptor 0,DataLen-1,DA_DRW
LABEL_DESC_VIDEO Descriptor 0B8000h,0ffffh,DA_DRW

GdtLen equ $-LABEL_GDT
GdtPtr dw GdtLen-1
            dd 0

;选择子
SelectorCode32  equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorCode16  equ LABEL_DESC_CODE16 - LABEL_GDT
SelectorStack32 equ LABEL_DESC_STACK32 - LABEL_GDT
SelectorData  equ LABEL_DESC_DATA - LABEL_GDT
SelectorVideo  equ LABEL_DESC_VIDEO - LABEL_GDT
SelectorNormal  equ LABEL_DESC_NORMAL - LABEL_GDT

[SECTION .idt]
ALIGN 32
[BITS 32]
LABEL_IDT:
%rep 32
    Gate SelectorCode32,SpuriousHandler,0,DA_386IGate
%endrep
.020h:  Gate SelectorCode32,ClockHandler,0,DA_386IGate
%rep 95
    Gate SelectorCode32,SpuriousHandler,0,DA_386IGate
%endrep
.080h: Gate SelectorCode32,UserHandler,0,DA_386IGate
IdtLen equ $ - LABEL_IDT
IdtPtr dd IdtLen-1
        dw 0


[SECTION .data]
ALIGN 32
[BITS 32]
LABEL_DATA:
_szPMMessage:   db "In Protect Mode now. ^-^", 0Ah, 0Ah, 0 ; 
_wSPValueInRealMode dd 0
szPMMessage equ _szPMMessage-$$
DataLen   equ $ - LABEL_DATA

; 全局堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK32:
 times 512 db 0
TopOfStack equ $ - LABEL_STACK32 - 1

;16位代码段，包括初始化分段机制的代码，加载GDT的代码，和返回实模式的代码
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
;jmp $
;实模式下访问各个段
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0100h

;修改代码段，记录实模式栈顶
mov [LABEL_GO_BACK_TO_REAL+3], ax
mov [_wSPValueInRealMode], sp

;开始检索内存
xor eax,eax
mov ax, cs
shl eax, 4
add eax, LABEL_SEG_CODE16
mov word [LABEL_DESC_CODE16 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE16 + 4], al
mov byte [LABEL_DESC_CODE16 + 7], ah

;初始化段描述符
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_SEG_CODE32
mov word [LABEL_DESC_CODE32+2],ax
shr eax,16
mov byte [LABEL_DESC_CODE32+4],al
mov byte [LABEL_DESC_CODE32+7],ah

xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_DATA
mov word [LABEL_DESC_DATA+2],ax
shr eax,16
mov byte [LABEL_DESC_DATA+4],al
mov byte [LABEL_DESC_DATA+7],ah

xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_STACK32
mov word [LABEL_DESC_STACK32 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_STACK32 + 4], al
mov byte [LABEL_DESC_STACK32 + 7], ah



;加载GTD
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_GDT
mov dword [GdtPtr + 2], eax

;加载IDT
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_IDT
mov dword [IdtPtr+2],eax

lgdt [GdtPtr]

;关中断
cli
lidt [IdtPtr]

;打开A20地址线，扩大寻址
in al,92h
or al,00000010b
out 92h,al

;设置cr0
mov eax,cr0
or eax,1
mov cr0,eax

;进入保护模式
jmp dword SelectorCode32:0

LABEL_REAL_ENTRY:  ; 从保护模式跳回到实模式就到了这里
;还原实模式状态
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, [_wSPValueInRealMode]

;关闭A20地址线
in al, 92h 
and al, 11111101b 
out 92h, al 

;开中断
sti

;回到DOS
mov ax, 4c00h
int 21h

;32位代码段，在保护模式中，我们开启分页模式
[SECTION .s32]
[BITS 32]

LABEL_SEG_CODE32:
;初始化各个段寄存器以适应保护模式
mov ax,SelectorData
mov ds,ax
mov es,ax
mov ax,SelectorVideo
mov gs,ax
mov ax,SelectorStack32
mov ss,ax
mov esp,TopOfStack

call Init8259A
int 080h
sti
jmp $
;返回实模式
jmp SelectorCode16:0

Init8259A:
    mov al,011h
    out 020h,al
    call io_delay

    out 0A0h,al
    call io_delay

    mov al,020h
    out 021h,al
    call io_delay

    mov al,028h
    out 0A1h,al
    call io_delay

    mov al, 004h 
    out 021h, al  
    call    io_delay

    mov al, 002h    ; 对应主8259的 IR2
    out 0A1h, al    ; 从8259, ICW3.
    call    io_delay

    mov al, 001h
    out 021h, al    ; 主8259, ICW4.
    call    io_delay

    out 0A1h, al    ; 从8259, ICW4.
    call    io_delay

    mov al, 11111110b   ; 仅仅开启定时器中断
;   mov al, 11111111b   ; 屏蔽主8259所有中断
    out 021h, al    ; 主8259, OCW1.
    call    io_delay

    mov al, 11111111b   ; 屏蔽从8259所有中断
    out 0A1h, al    ; 从8259, OCW1.
    call    io_delay
    ret

io_delay:
    nop
    nop
    nop
    nop
    ret

_SpuriousHandler:
SpuriousHandler equ _SpuriousHandler - $$
    mov ah, 0Ch             ; 0000: 黑底    1100: 红字
    mov al, '!'
    mov [gs:((80 * 0 + 75) * 2)], ax    ; 屏幕第 0 行, 第 75 列。
;   jmp $
    iretd

_UserHandler:
UserHandler  equ _UserHandler - $$
    mov ah, 0Ch
    mov al, 'D'
    mov [gs:((80 * 0 + 75) * 2)], ax    ; 屏幕第 0 行, 第 75 列。
    iretd

_ClockHandler:
ClockHandler    equ _ClockHandler - $$
    inc byte [gs:((80*0+75)*2)]
    mov     al, 20h
    out 20h, al    ;发送EOI end of interrupt 表示中断任务处理完成
    iretd

SegCode32Len    equ $-LABEL_SEG_CODE32

; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:
 ; 跳回实模式:
 mov ax, SelectorNormal
 mov ds, ax
 mov es, ax
 mov fs, ax
 mov gs, ax
 mov ss, ax

 mov eax, cr0
 and     eax, 7FFFFFFEh          ; PE=0, PG=0
 mov cr0, eax

LABEL_GO_BACK_TO_REAL:
 jmp 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值

Code16Len equ $ - LABEL_SEG_CODE16

; END of [SECTION .s16code]